/*  SSEventView.m
 *  MESLib
 * 
 * Copyright 2003-2009 Starline Software Ltd
 * 
 * This file is part of MESLib.
 * 
 * MESLib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MESLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with MESLib. If not, see <http://www.gnu.org/licenses/>.
 */
//  Created by Tobias Haughton on Wed Jan 22 2003.
//  Copyright (c) 2003 Starline Software. All rights reserved.


//the SSEventView object displays information about it's SSEventObject owner in a form which is
//easily viewed by program users. it has 2 modes for doing this, matrix mode where the info is
//displayed directly in the view and overlap mode where the views sizes based on owners begin and end dates 


#import "SSEventView.h"
#import "SSEventObject.h"
#import "SSChartView.h"
#import "SSEventLinkObject.h"
#import "SSCatalystDocument.h"
#import "SSInspectorController.h"

@implementation SSEventView

- (id)initWithFrame:(NSRect)frame owner:(SSEventObject *)own isGhostView:(BOOL)ghost {
    self = [super initWithFrame:frame];
   
    if (self) {
       [self setPostsFrameChangedNotifications:YES]; //post a notification when our frame changes

       NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
       [nc addObserver:self
              selector:@selector(setConnectionPoints)
                  name:@"NSViewFrameDidChangeNotification"
                object:self];
       [nc addObserver:self
              selector:@selector(setupForDisplayAfterPreferenceChange:)
                  name:@"SSUpdateEBDisplay"
                object:nil];

       dateFormatter = [[SSEBDateFormatter alloc] initWithDateFormat:[NSString stringWithFormat:@"%@:%@:%@ %@:%@:%@:%@",
          @"%Y", @"%m", @"%d", @"%H", @"%M", @"%S", @"%F"] allowNaturalLanguage:NO];
       sourceFormatter = [[SSSourceFormatter alloc] init];

       stretchCursor = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"stretchCursor.tiff"] hotSpot:NSMakePoint(8, 8)];
       
       [self setIsGhostView:ghost];
       [self setOwner:own];
       [self setLastXPosition];
       [self setDraggingMode:SSNoActionDraggingMode];
    }

    return self;
}

- (id)initWithEventView:(SSEventView *)view {
   self = [super initWithFrame:[view frame]];
   if (self) {
      NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
      [nc addObserver:self
             selector:@selector(setConnectionPoints)
                 name:@"NSViewFrameDidChangeNotification"
               object:self];
      [nc addObserver:self
             selector:@selector(setupForDisplayAfterPreferenceChange:)
                 name:@"SSUpdateEBDisplay"
               object:nil];
      
      dateFormatter = [[SSEBDateFormatter alloc] initWithDateFormat:[NSString stringWithFormat:@"%@:%@:%@ %@:%@:%@:%@",
         @"%Y", @"%m", @"%d", @"%H", @"%M", @"%S", @"%F"] allowNaturalLanguage:NO];

      lastXPosition = [view lastXPosition];
      
      [self setOwner:[view owner]];
      [self setIsGhostView:[view isGhostView]];
      [self setDraggingMode:SSNoActionDraggingMode];
   }
   return self;
}

+ (int)numberOfDisplayedFields
{
   //return the number of fields displayed in event views
   int fields = SSMinNumberOfEBFields;
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

   if([defaults boolForKey:SSDisplayEBActorKey]) {
      fields++;
   }
   
   if([defaults boolForKey:SSDisplayEBObjectKey]) {
      fields++;
   }

   if([defaults boolForKey:SSDisplayEBSourceKey]) {
      fields++;
   }

   if([defaults boolForKey:SSDisplayEBLocationKey]) {
      fields++;
   }

   if([defaults boolForKey:SSDisplayEBBeginDateKey]) {
       fields++;
   }

   if([defaults boolForKey:SSDisplayEBEndDateKey]) {
       fields++;
   }

   return fields;
}

- (void)drawRect:(NSRect)rect {
   if([owner isVisible]) {
      //get our color based on our owners label
      switch([[owner label] intValue]) {
         case SSYellowLabelType:[SSYellowColor set];
            break;
         case SSGreenLabelType:[SSGreenColor set];
            break;
         case SSLightBlueLabelType:[SSLightBlueColor set];
            break;
         case SSBlueLabelType:[SSBlueColor set];
            break;
         case SSPurpleLabelType:[SSPurpleColor set];
            break;
         case SSOrangeLabelType:[SSOrangeColor set];
            break;
         case SSRedLabelType:[SSRedColor set];
            break;
         default:
            [SSWhiteColor set];
            break;
      }

      //fill in our background
      [NSBezierPath fillRect:[self bounds]];

      [[NSColor blackColor]set];
      [borderPath stroke]; //draw the boarder

      if(isGhostView) {
         [addressString drawAtPoint:NSMakePoint(5, (NSHeight([self frame]) - 15))];
         [questionString drawAtPoint:NSMakePoint(55, 0)];
      } else {
         [gridPath stroke]; //draw the grid
      }
      
      if(isSelected) {
         //show that we are selected
         NSBezierPath *selectedPath = [NSBezierPath bezierPathWithRect:[self bounds]];
         [selectedPath setLineWidth:10];
         [SSSelectedColor set];
         [selectedPath stroke];
      }      
   }
}

//***********************************

- (void)resetCursorRects
{
   //set our cursor rects
   if(ssDisplayMode == SSMatrixDisplayMode) {
      //just use the arrow cursor
      [self addCursorRect:NSMakeRect([self visibleRect].origin.x + 3, [self visibleRect].origin.y,
                                     NSWidth([self visibleRect]) - 6, NSHeight([self visibleRect])) cursor:[NSCursor arrowCursor]];
   } else {
      [self addCursorRect:NSMakeRect([self visibleRect].origin.x + 3, [self visibleRect].origin.y,
                                     NSWidth([self visibleRect]) - 6, NSHeight([self visibleRect])) cursor:[NSCursor arrowCursor]];

      [self addCursorRect:NSMakeRect([self visibleRect].origin.x, [self visibleRect].origin.y,
                                     3, NSHeight([self visibleRect])) cursor:stretchCursor];

      [self addCursorRect:NSMakeRect(([self visibleRect].origin.x + ([self visibleRect].size.width - 3)),
                                     [self visibleRect].origin.y, 3, NSHeight([self visibleRect])) cursor:stretchCursor];
   }
}

//***********************************mouse events

- (void)mouseDown:(NSEvent *)theEvent
{
   NSPoint p = [theEvent locationInWindow];
   NSPoint converted = [[self superview] convertPoint:p fromView:nil];
   NSPoint convertedForSelf = [self convertPoint:p fromView:nil];

   if(([theEvent modifierFlags] & NSCommandKeyMask) || ([theEvent modifierFlags] & NSShiftKeyMask)) {
      if(isSelected) { //already selected we want to remove our selected status
         [(SSChartView *)[self superview] invalidateSelectionForObject:self];
      } else { //using command key they are selecting multiple views
         [(SSChartView *)[self superview] markAsSelected:self byAddingToSelection:YES];
      }
   } else { //no command just select us
      [(SSChartView *)[self superview] markAsSelected:self byAddingToSelection:NO];
   }

   [(SSChartView *)[self superview] setSelectedViewsDistanceFromOrigin:converted];
   
   if([self selectedField]) { //stop the currently selected text field from being edited
      [self setSelectedField:nil];
   }

   if([theEvent clickCount] == 2 && !([theEvent modifierFlags] & NSCommandKeyMask)) { //if user double clicks see if it is above one of our text fields and if so allow editing
      if(ssDisplayMode == SSMatrixDisplayMode) {
         //allow editing
         [self allowEditingInFieldAtPoint:p];
      } else {
         //open the inspector
         [[SSInspectorController sharedInspectorController] showWindow:self];
      }
   }

   if(([theEvent modifierFlags] & NSControlKeyMask)) {//user wants a menu
      [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self];
   } else if(ssDisplayMode == SSMatrixDisplayMode) {
      //in matrix view so we only drag the whole thing
      [(SSChartView *)[self superview] setSelectedEventDraggingMode:SSMovingWholeDraggingMode];
   } else if(convertedForSelf.x < 4) {
      //user is trying to change the start time of the EB
      [(SSChartView *)[self superview] setSelectedEventDraggingMode:SSChangingStartDraggingMode];
   } else if(convertedForSelf.x > NSWidth([self frame]) - 3) {
      //user is trying to change end time of EB
      [(SSChartView *)[self superview] setSelectedEventDraggingMode:SSChangingEndDraggingMode];
   } else { //they are dragging the whole thing
      [(SSChartView *)[self superview] setSelectedEventDraggingMode:SSMovingWholeDraggingMode];
   }
}


- (void)rightMouseDown:(NSEvent *)theEvent
{
   [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self];
}

- (void)mouseDragged:(NSEvent *)theEvent
{
   NSPoint p = [theEvent locationInWindow];
   NSPoint converted = [[self superview] convertPoint:p fromView:nil];
   NSPoint startPoint;
   NSPoint endPoint;

   if(([theEvent modifierFlags] & NSCommandKeyMask)) { //user is making link
      [(SSChartView *)[self superview] setTemporaryLinkWithStartPoint:[self centerPoint] endPoint:converted];
      [self setIsLinking:YES];

      //scroll superview
      [[[self superview] superview] autoscroll:theEvent];
   } else { //they are dragging
      if(ssDisplayMode == SSMatrixDisplayMode) {
         //in matrix so can only drag the whole thing
         [(SSChartView *)[self superview] setSelectedViewsOriginWithPoint:converted];
      } else if(draggingMode == SSChangingStartDraggingMode) {
         //user is in overlap so act based on our mode
         if(converted.x < NSMaxX([self frame])) { //can't drag past the end of our view
            startPoint = NSMakePoint(converted.x, [self frame].origin.y);

            [self setFrameSize:NSMakeSize((([self frame].origin.x + [self frame].size.width) - startPoint.x),
                                          [self frame].size.height)];
            [self setFrameOrigin:startPoint];

            [self setPaths];
            
            [[self superview] displayRect:[[self superview] visibleRect]];
            [[[self superview] superview] autoscroll:theEvent];
         }
      } else {
         if(draggingMode == SSChangingEndDraggingMode) {
            if(converted.x > NSMinX([self frame])) {
               endPoint = NSMakePoint(converted.x, [self frame].origin.y);
               [self setFrameSize:NSMakeSize((endPoint.x - [self frame].origin.x),
                                             [self frame].size.height)];

               [self setPaths];

               [[self superview] displayRect:[[self superview] visibleRect]];
               [[[self superview] superview] autoscroll:theEvent];
            }
         } else { //they're moving the whole thing
            [(SSChartView *)[self superview] setSelectedViewsOriginWithPoint:converted];
         }
      }
   }
}

- (void)mouseUp:(NSEvent *)theEvent
{
   NSPoint p = [theEvent locationInWindow];
   NSPoint converted = [[self superview] convertPoint:p fromView:nil];

   if([self isLinking]) {
      //we are trying to make a link
      [(SSChartView *)[self superview] setLinkIfAboveAnotherEB:self point:converted];
      [(SSChartView *)[self superview] removeTemporaryLinkPoints];
   } else if(ssDisplayMode == SSOverlapDisplayMode) {
      //in overlap, set dates if needed
      if(draggingMode == SSMovingWholeDraggingMode) { //dragging the whole view
         [(SSChartView *)[self superview] updateSelectedEventDataAfterDrag];
      } else if(draggingMode == SSChangingStartDraggingMode) { //changing the start date
         SSChartDate *begin = [(SSChartView *)[self superview] dateForXPosition:NSMinX([self frame]) isEstimated:[[owner beginTime] isEstimated]];
         if([[owner beginTime] compare:begin] != NSOrderedSame) {
            //only set the date if it is different
            [owner setBeginTime:begin];
         }
      } else if(draggingMode == SSChangingEndDraggingMode) { //changing the end date
         SSChartDate *end = [(SSChartView *)[self superview] dateForXPosition:NSMaxX([self frame]) isEstimated:[[owner endTime] isEstimated]];
         if([[owner endTime] compare:end] != NSOrderedSame) {
            //only set the date if it is different
            [owner setEndTime:end];
         }
      }
   }

   [(SSChartView *)[self superview] setSelectedEventDraggingMode:SSNoActionDraggingMode];
}

//***********************************
- (void)updateDatesAfterDrag
{
   SSChartDate *begin = [(SSChartView *)[self superview] dateForXPosition:NSMinX([self frame]) isEstimated:[[owner beginTime] isEstimated]];
   SSChartDate *end = [(SSChartView *)[self superview] dateForXPosition:NSMaxX([self frame]) isEstimated:[[owner endTime] isEstimated]];

   if([[owner beginTime] compare:begin] != NSOrderedSame) {
      //only set the date if it is different
      [owner setBeginTimeNoWarning:begin];
   }

   if([[owner endTime] compare:end] != NSOrderedSame) {
      //only set the date if it is different
      [owner setEndTimeNoWarning:end];
   }
}

//***********************************

- (void)setDistanceFromOrigin:(NSPoint)point
{
   distanceFromOrigin.x = point.x - [self frame].origin.x;
   distanceFromOrigin.y = point.y - [self frame].origin.y;
}

- (void)setNewOriginWithPoint:(NSPoint)point
{
   NSPoint newOrigin = NSMakePoint(point.x - distanceFromOrigin.x, NSMinY([self frame]));
   NSRect oldFrame = [self frame];
   [self setFrameOrigin:newOrigin];

   if([[owner linkArray] count] == 0 && [[owner linkFromArray] count] == 0) {
      //we have no links so just update the display for our frame + the area we moved from
      if(newOrigin.x > NSMinX(oldFrame)) {
         //we are moving right
         NSRect displayArea = NSMakeRect((NSMinX(oldFrame) - 1), 0, (NSMaxX([self frame]) - NSMinX(oldFrame) + 25), (NSMaxY(oldFrame) + 25));
         [[self superview] setNeedsDisplayInRect:displayArea];
      } else {
         NSRect displayArea = NSMakeRect((newOrigin.x - 1), 0, (NSMaxX(oldFrame) - NSMinX([self frame]) + 25), (NSMaxY([self frame]) + 25));
         [[self superview] setNeedsDisplayInRect:displayArea];
      }
   } else {
      //we have links so update the whole visible area
      [[self superview] setNeedsDisplayInRect:[[self superview] visibleRect]];
   }

   if([self displayMode] == SSMatrixDisplayMode) {
      //set a timer so that we set undo after we have been unmoved for 1 second
      //if in overlap undo is handled in our owner whent the dates are set
      [undoMovementTimer invalidate];
      [undoMovementTimer release];
      undoMovementTimer = [[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(setMovementUndo:) userInfo:nil repeats:NO] retain];
   }

   [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
}

- (void)setNewOriginWithValue:(NSValue *)value
{
   NSPoint newOrigin = [value pointValue];
   NSRect oldFrame = [self frame];
   [self setFrameOrigin:newOrigin];

   if([[owner linkArray] count] == 0 && [[owner linkFromArray] count] == 0) {
      //we have no links so just update the display for our frame + the area we moved from
      if(newOrigin.x > NSMinX(oldFrame)) {
         //we are moving right
         NSRect displayArea = NSMakeRect((NSMinX(oldFrame) - 1), 0, (NSMaxX([self frame]) - NSMinX(oldFrame) + 25), (NSMaxY(oldFrame) + 25));
         [[self superview] setNeedsDisplayInRect:displayArea];
      } else {
         NSRect displayArea = NSMakeRect((newOrigin.x - 1), 0, (NSMaxX(oldFrame) - NSMinX([self frame]) + 25), (NSMaxY([self frame]) + 25));
         [[self superview] setNeedsDisplayInRect:displayArea];
      }
   } else {
      //we have links so update the whole visible area
      [[self superview] setNeedsDisplayInRect:[[self superview] visibleRect]];
   }

   [self setMovementUndo:nil];

   [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
}

//***********************************
- (int)displayMode
{
   return ssDisplayMode;
}

- (void)setDisplayMode:(int)mode
{
   if(mode == SSMatrixDisplayMode) {
      [self setFrameOrigin:NSMakePoint(lastXPosition, NSMinY([self frame]))];
   } else {
      [self setSelectedField:nil]; //stop editing
      [self setLastXPosition];
   }
   
   ssDisplayMode = mode;
   
   [self setupForDisplay];
   [self updateDisplayValues];
}

//***********************************
- (void)setupForDisplay
{
   if(isGhostView) {
      [self setGhostStrings];
   } else {
      if(ssDisplayMode == SSOverlapDisplayMode) {
         [self setFrame:[(SSChartView *)[self superview] frameForDates:[owner beginTime]
                                                                   end:[owner endTime]
                                                              oldFrame:[self frame]]];
      }

      [self setTextFields];
   }

   [self setViewSize];
   [self setPaths];
   [self setConnectionPoints];

   [self setNeedsDisplay:YES];
}

- (void)setupForDisplayAfterPreferenceChange:(NSNotification *)note
{
   [self setViewSize];
   [self setConnectionPoints];
   [self setPaths];
   
   if([self displayMode] == SSMatrixDisplayMode) {
      if(isGhostView) {
         [self setGhostStrings];
      } else {
         //the cell is sent as the notes object
         //workout which field should be changed and pass the cell state as the argument for whether it should be displayed or not
         switch([[note object] tag]) {
            case preferenceShowSourceFieldTag:[self changeSourceFieldDisplay:[[note object] state]];
               break;
            case preferenceShowObjectFieldTag:[self changeObjectFieldDisplay:[[note object] state]];
               break;
            case preferenceShowLocationFieldTag:[self changeLocationFieldDisplay:[[note object] state]];
               break;
            case preferenceShowBeginDateFieldTag:[self changeBeginDateFieldDisplay:[[note object] state]];
               break;
            case preferenceShowEndDateFieldTag:[self changeEndDateFieldDisplay:[[note object] state]];
               break;
            case preferenceShowActorFieldTag:[self changeActorFieldDisplay:[[note object] state]];
               break;
            default:
               break;
         }
      }
   } else {
      //in overlap
      if(isGhostView) {
         [self setGhostStrings];
      } else {
         switch([[note object] tag]) {
            case preferenceShowBeginDateFieldTag:[self changeBeginDateFieldDisplay:[[note object] state]];
               break;
            case preferenceShowEndDateFieldTag:[self changeEndDateFieldDisplay:[[note object] state]];
               break;
            default:
               break;
         }
      }      
   }

   [self setNeedsDisplay:YES];
}

- (void)updateAfterOverlapViewChange
{
   //ask for a new frame to fit in with the current scale
   [self setFrame:[(SSChartView *)[self superview] frameForDates:[owner beginTime]
                                                             end:[owner endTime]
                                                        oldFrame:[self frame]]];
   [self setPaths];
}

- (void)updateOverlapFrameAfterExternalDateChange
{
   [self setFrame:[(SSChartView *)[self superview] frameForDates:[owner beginTime]
                                                             end:[owner endTime]
                                                        oldFrame:[self frame]]];
   [self setPaths];
   [[self superview] setNeedsDisplayInRect:[[self superview] visibleRect]];
}

- (void)updateDatesAfterExternalPositionChange
{
   //used when our x position is changed through the inspector or eb panel
   SSChartDate *begin = [(SSChartView *)[self superview] dateForXPosition:NSMinX([self frame]) isEstimated:[[owner beginTime] isEstimated]];
   SSChartDate *end = [(SSChartView *)[self superview] dateForXPosition:NSMaxX([self frame]) isEstimated:[[owner endTime] isEstimated]];

   [owner setBeginTime:begin];
   [owner setEndTime:end];
}

- (SSEventObject *)owner
{
   return owner;   
}

- (void)setOwner:(SSEventObject *)own
{
   owner = own;
}

- (void)setViewSize
{
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   
   if(ssDisplayMode == SSMatrixDisplayMode) {
      numFields = SSMinNumberOfEBFields;

      if([defaults boolForKey:SSDisplayEBActorKey]) {
         numFields++;
      }

      if([defaults boolForKey:SSDisplayEBSourceKey]) {
         numFields++;
      }

      if([defaults boolForKey:SSDisplayEBObjectKey]) {
         numFields++;
      }

      if([defaults boolForKey:SSDisplayEBLocationKey]) {
         numFields++;
      }

      if([defaults boolForKey:SSDisplayEBBeginDateKey]) {
         numFields++;
      }

      if([defaults boolForKey:SSDisplayEBEndDateKey]) {
         numFields++;
      }

      [self setFrameSize:NSMakeSize(SSEventViewDefaultSize.width, ((numFields * SSDistanceForEventFields) + 6))];
   } else {
      //in overlap mode
      if([defaults boolForKey:SSDisplayEBBeginDateKey] || [defaults boolForKey:SSDisplayEBEndDateKey]) {
         [self setFrameSize:NSMakeSize(NSWidth([self frame]), SSEventViewOverlapModeWithDatesHeight)];
      } else {
         [self setFrameSize:NSMakeSize(NSWidth([self frame]), SSEventViewOverlapModeNoDatesHeight)];
      }
   }
}

- (void)setTextFields
{
   //setTextFields sets the text fields when the EB is first created or loaded. it should not be used to alter the EBs appearence after creation
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   NSEnumerator *enumerator = [[self subviews] objectEnumerator];
   id anObject;

   while (anObject = [enumerator nextObject]) {
      [anObject removeFromSuperview];
   }
   
   //no option to remove this field
   [self changeActionFieldDisplay:YES];
   
   //only display these fields if user has selected to
   if([defaults boolForKey:SSDisplayEBBeginDateKey]) {
      [self changeBeginDateFieldDisplay:YES];
   }

   if([defaults boolForKey:SSDisplayEBEndDateKey]) {
      [self changeEndDateFieldDisplay:YES];
   }

   if([self displayMode] == SSMatrixDisplayMode) {
      //these fields can only be displayed in matrix mode
      if([defaults boolForKey:SSDisplayEBActorKey]) {
         //user wants object field
         [self changeActorFieldDisplay:YES];
      }

      if([defaults boolForKey:SSDisplayEBObjectKey]) {
         //user wants object field
         [self changeObjectFieldDisplay:YES];
      }

      if([defaults boolForKey:SSDisplayEBSourceKey]) {
         //user wants source field
         [self changeSourceFieldDisplay:YES];
      }

      if([defaults boolForKey:SSDisplayEBLocationKey]) {
         //user wants location field
         [self changeLocationFieldDisplay:YES];
      }
      
   }
}

- (void)changeActorFieldDisplay:(BOOL)shouldDisplay
{
   if(shouldDisplay) {
      //we want to add this field
      actorField = [[[SSEventTextField alloc]
          initWithFrame:NSMakeRect(0, 0, (SSEventViewDefaultSize.width - 6), SSEventFieldHeight)] autorelease];
      [actorField setStringValue:[owner actor]];

      NSFont *font = [NSFont fontWithName:@"Helvetica" size:10];
      [[actorField cell] setBordered:NO];
      [[actorField cell] setFont:font];
      [actorField setSelectable:NO];
      [actorField setDelegate:self];
      [actorField setDrawsBackground:NO];
      [actorField setToolTip:NSLocalizedString(@"ActorField", nil)];
      [actorField setTag:1];
      [self addSubview:actorField];

      if([owner actor]) {
         [actorField setStringValue:[owner actor]];
      }

      [[SSTextEntryValidationMK2 sharedTextEntryValidation] addWatchedControl:actorField];
   } else {
      [[SSTextEntryValidationMK2 sharedTextEntryValidation] removeWatchedControl:actorField];
      [actorField removeFromSuperview];
      actorField = nil;
   }
   
   [self arrangeSubviews];
}

- (void)changeActionFieldDisplay:(BOOL)shouldDisplay
{
   if(shouldDisplay) {
      //we want to add this field
      actionField = [[[SSEventTextField alloc]
          initWithFrame:NSMakeRect(0, 0, (SSEventViewDefaultSize.width - 6), SSEventFieldHeight)] autorelease];

      NSFont *font = [NSFont fontWithName:@"Helvetica" size:10];
      [[actionField cell] setBordered:NO];
      [[actionField cell] setFont:font];
      [actionField setSelectable:NO];
      [actionField setDelegate:self];
      [actionField setDrawsBackground:NO];
      [actionField setToolTip:NSLocalizedString(@"ActionField", nil)];
      [actionField setTag:2];
      [self addSubview:actionField];

      if([self displayMode] == SSMatrixDisplayMode) {
         if([owner action]) {
            [actionField setStringValue:[owner action]];
         }
      } else {
         //in overlap
         [actionField setStringValue:[NSString stringWithFormat:@"%@ - %@", [owner action], [owner object]]];
         //change action filed frame size
         [actionField setFrameSize:NSMakeSize((NSWidth([self frame]) - 6), SSEventFieldOverlapHeight)];

         [[actionField cell] setWraps:YES];
      }

      [[SSTextEntryValidationMK2 sharedTextEntryValidation] addWatchedControl:actionField];
   } else {
      [[SSTextEntryValidationMK2 sharedTextEntryValidation] removeWatchedControl:actionField];
      [actionField removeFromSuperview];
      actionField = nil;
   }

   [self arrangeSubviews];
}

- (void)changeSourceFieldDisplay:(BOOL)shouldDisplay
{
   if(shouldDisplay) {
      //we want to add this field
      sourceField = [[[SSEventTextField alloc]
          initWithFrame:NSMakeRect(0, 0, (SSEventViewDefaultSize.width - 6), SSEventFieldHeight)] autorelease];
      
      NSFont *font = [NSFont fontWithName:@"Helvetica" size:10];
      [[sourceField cell] setBordered:NO];
      [[sourceField cell] setFont:font];
      [sourceField setSelectable:NO];
      [sourceField setDelegate:self];
      [sourceField setDrawsBackground:NO];
      [sourceField setToolTip:NSLocalizedString(@"SourceField", nil)];
      [sourceField setTag:0];
      [sourceField setFormatter:sourceFormatter];
      [self addSubview:sourceField];

      if([owner source]) {
         [sourceField setObjectValue:[owner source]];
      }

      [[SSTextEntryValidationMK2 sharedTextEntryValidation] addWatchedControl:sourceField];
   } else {
      [[SSTextEntryValidationMK2 sharedTextEntryValidation] removeWatchedControl:sourceField];
      [sourceField removeFromSuperview];
      sourceField = nil;
   }
   [self arrangeSubviews];
}

- (void)changeLocationFieldDisplay:(BOOL)shouldDisplay
{
   if(shouldDisplay) {
      //we want to add this field
      locationField = [[[SSEventTextField alloc]
          initWithFrame:NSMakeRect(0, 0, (SSEventViewDefaultSize.width - 6), SSEventFieldHeight)] autorelease];
      
      NSFont *font = [NSFont fontWithName:@"Helvetica" size:10];
      [[locationField cell] setBordered:NO];
      [[locationField cell] setFont:font];
      [locationField setSelectable:NO];
      [locationField setDelegate:self];
      [locationField setToolTip:NSLocalizedString(@"LocationField", nil)];
      [locationField setTag:6];
      [locationField setDrawsBackground:NO];
      [self addSubview:locationField];

      if([owner location]) {
         [locationField setStringValue:[owner location]];
      }
      [[SSTextEntryValidationMK2 sharedTextEntryValidation] addWatchedControl:locationField];
   } else {
      [[SSTextEntryValidationMK2 sharedTextEntryValidation] removeWatchedControl:locationField];
      [locationField removeFromSuperview];
      locationField = nil;
   }
   [self arrangeSubviews];
}

- (void)changeObjectFieldDisplay:(BOOL)shouldDisplay
{
   if(shouldDisplay) {
      //we want to add this field
      objectField = [[[SSEventTextField alloc]
          initWithFrame:NSMakeRect(0, 0, (SSEventViewDefaultSize.width - 6), SSEventFieldHeight)] autorelease];
   
      NSFont *font = [NSFont fontWithName:@"Helvetica" size:10];
      [[objectField cell] setBordered:NO];
      [[objectField cell] setFont:font];
      [objectField setSelectable:NO];
      [objectField setDelegate:self];
      [objectField setToolTip:NSLocalizedString(@"ObjectField", nil)];
      [objectField setTag:5];
      [objectField setDrawsBackground:NO];
      [self addSubview:objectField];

      if([owner object]) {
         [objectField setStringValue:[owner object]];
      }
      [[SSTextEntryValidationMK2 sharedTextEntryValidation] addWatchedControl:objectField];
   } else {
      [[SSTextEntryValidationMK2 sharedTextEntryValidation] removeWatchedControl:objectField];
      [objectField removeFromSuperview];
      objectField = nil;
   }
   [self arrangeSubviews];
}

- (void)changeBeginDateFieldDisplay:(BOOL)shouldDisplay
{
   if(shouldDisplay) {
      //we want to add this field
      if([self displayMode] == SSMatrixDisplayMode) {
         beginDateField = [[[SSEventTextField alloc]
          initWithFrame:NSMakeRect(0, 0, (SSEventViewDefaultSize.width - 6), SSEventFieldHeight)] autorelease];
      } else {
         beginDateField = [[[SSEventTextField alloc]
          initWithFrame:NSMakeRect(0, 0, ((NSWidth([self frame]) / 2) - 4.5), SSEventFieldOverlapHeight)] autorelease];
         [[beginDateField cell] setWraps:YES];
      }

      NSFont *font = [NSFont fontWithName:@"Helvetica" size:10];
      [[beginDateField cell] setBordered:NO];
      [[beginDateField cell] setFont:font];
      [beginDateField setSelectable:NO];
      [beginDateField setDelegate:self];
      [beginDateField setToolTip:NSLocalizedString(@"BeginDateField", nil)];
      [beginDateField setTag:3];
      [beginDateField setDrawsBackground:NO];
      [beginDateField setFormatter:dateFormatter];
      [self addSubview:beginDateField];

      if([owner beginTime]) {
         [beginDateField setObjectValue:[owner beginTime]];
      }
   } else {
      [beginDateField removeFromSuperview];
      beginDateField = nil;
   }
   [self arrangeSubviews];
}

- (void)changeEndDateFieldDisplay:(BOOL)shouldDisplay
{
   if(shouldDisplay) {
      //we want to add this field
      if([self displayMode] == SSMatrixDisplayMode) {
         endDateField = [[[SSEventTextField alloc]
          initWithFrame:NSMakeRect(0, 0, (SSEventViewDefaultSize.width - 6), SSEventFieldHeight)] autorelease];
      } else {
         endDateField = [[[SSEventTextField alloc]
          initWithFrame:NSMakeRect(0, 0, ((NSWidth([self frame]) / 2) - 4.5), SSEventFieldOverlapHeight)] autorelease];
         [[endDateField cell] setWraps:YES];
         [[endDateField cell] setAlignment:NSRightTextAlignment];
      }

      NSFont *font = [NSFont fontWithName:@"Helvetica" size:10];
      [[endDateField cell] setBordered:NO];
      [[endDateField cell] setFont:font];
      [endDateField setSelectable:NO];
      [endDateField setDelegate:self];
      [endDateField setToolTip:NSLocalizedString(@"EndDateField", nil)];
      [endDateField setTag:4];
      [endDateField setDrawsBackground:NO];
      [endDateField setFormatter:dateFormatter];
      [self addSubview:endDateField];

      if([owner endTime]) {
         [endDateField setObjectValue:[owner endTime]];
      }
   } else {
      [endDateField removeFromSuperview];
      endDateField = nil;
   }

   [self arrangeSubviews];
}


- (void)arrangeSubviews
{
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   BOOL displayActor = [defaults boolForKey:SSDisplayEBActorKey];
   BOOL displayObject = [defaults boolForKey:SSDisplayEBObjectKey];
   BOOL displaySource = [defaults boolForKey:SSDisplayEBSourceKey];
   BOOL displayLocation = [defaults boolForKey:SSDisplayEBLocationKey];
   BOOL displayBeginDate = [defaults boolForKey:SSDisplayEBBeginDateKey];

   NSPoint theOrigin;
   NSEnumerator *enumerator = [[self subviews] objectEnumerator];
   id field;
   int num = 0;

   while (field = [enumerator nextObject]) {
      //loop through the displayed fields and position them accordingly
      num = SSMinNumberOfEBFields;
      if([field isEqualTo:actorField]) {
         theOrigin = NSMakePoint(3, 3);
         [field setFrameOrigin:theOrigin];
      } else if([field isEqualTo:actionField]) {
         if([self displayMode] == SSMatrixDisplayMode) {
            if(displayActor) {
               theOrigin = NSMakePoint(3, (SSDistanceForEventFields + 3));
            } else {
               theOrigin = NSMakePoint(3, 3);
            }
         } else {
            //in overlap so this is the top field
            theOrigin = NSMakePoint(3, 3);
         }

         [field setFrameOrigin:theOrigin];
      } else if([field isEqualTo:objectField]) {
         if(displayActor) {
            num++;
         }
         theOrigin = NSMakePoint(3, ((num * SSDistanceForEventFields) + 3));
         [field setFrameOrigin:theOrigin];
      } else if([field isEqualTo:locationField]) {
         if(displayActor) {
            num++;
         }
         if(displayObject) {
            num++;
         }
         theOrigin = NSMakePoint(3, ((num * SSDistanceForEventFields) + 3));
         [field setFrameOrigin:theOrigin];
      } else if([field isEqualTo:sourceField]) {
         if(displayActor) {
            num++;
         }
         if(displayObject) {
            num++;
         }
         if(displayLocation) {
            num++;
         }
         theOrigin = NSMakePoint(3, ((num * SSDistanceForEventFields) + 3));
         [field setFrameOrigin:theOrigin];
      } else if([field isEqualTo:beginDateField]) {
         if([self displayMode] == SSMatrixDisplayMode) {
            if(displayActor) {
               num++;
            }
            if(displayObject) {
               num++;
            }
            if(displaySource) {
               num++;
            }
            if(displayLocation) {
               num++;
            }
            theOrigin = NSMakePoint(3, ((num * SSDistanceForEventFields) + 3));
         } else {
            //in overlap
            theOrigin = NSMakePoint(3, (SSDistanceForOverlapEventFields + 3));
         }
         [field setFrameOrigin:theOrigin];
      } else if([field isEqualTo:endDateField]) {
         if([self displayMode] == SSMatrixDisplayMode) {
            if(displayActor) {
               num++;
            }
            if(displayObject) {
               num++;
            }
            if(displaySource) {
               num++;
            }
            if(displayLocation) {
               num++;
            }
            if(displayBeginDate) {
               num++;
            }
            theOrigin = NSMakePoint(3, ((num * SSDistanceForEventFields) + 3));
         } else {
            //in overlap
            theOrigin = NSMakePoint(((NSWidth([self frame]) / 2) + 1.5), (SSDistanceForOverlapEventFields + 3));
         }
         [field setFrameOrigin:theOrigin];
      }
   }
}

- (void)setPaths
{
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   
   [borderPath release];
   [gridPath release];

   borderPath = nil;
   gridPath = nil;
   
   if(ssDisplayMode == SSMatrixDisplayMode) {
      int rowNum = 1;

      if([defaults boolForKey:SSDisplayEBActorKey]) {
         rowNum++;
      }

      if([defaults boolForKey:SSDisplayEBObjectKey]) {
         rowNum++;
      }

      int i = (numFields - rowNum);

      borderPath = [NSBezierPath bezierPathWithRect:[self bounds]];
      [borderPath retain];
      [borderPath setLineWidth:1];

      gridPath = [[NSBezierPath alloc] init];
      [gridPath setLineWidth:.5];

      while(i > 0) {
         [gridPath moveToPoint:NSMakePoint(0, ((rowNum * SSDistanceForEventFields) + 1))];
         [gridPath lineToPoint:NSMakePoint(NSWidth([self frame]), ((rowNum * SSDistanceForEventFields) + 1))];
         i--;
         rowNum++;
      }
   } else {
      borderPath = [NSBezierPath bezierPathWithRect:[self bounds]];
      [borderPath retain];
      [borderPath setLineWidth:1];

      if([defaults boolForKey:SSDisplayEBBeginDateKey] || [defaults boolForKey:SSDisplayEBEndDateKey]) {
         gridPath = [[NSBezierPath alloc] init];
         [gridPath setLineWidth:.5];

         [gridPath moveToPoint:NSMakePoint(0, NSMaxY([actionField frame]) + 1)];
         [gridPath lineToPoint:NSMakePoint(NSWidth([self frame]), NSMaxY([actionField frame]) + 1)];

         if([defaults boolForKey:SSDisplayEBBeginDateKey] && [defaults boolForKey:SSDisplayEBEndDateKey]) {
            // we are displaying begin and end times draw a divider
            [gridPath moveToPoint:NSMakePoint((NSWidth([self frame]) / 2), NSMaxY([actionField frame]) + 1)];
            [gridPath lineToPoint:NSMakePoint((NSWidth([self frame]) / 2), NSMaxY([self frame]))];
         }
      }
   }
}

- (void)setGhostStrings
{
   questionString = [[NSAttributedString alloc] initWithString:@"?"
                                                    attributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSColor redColor],
                                                       NSForegroundColorAttributeName,
                                                       [NSFont fontWithName:@"Helvetica" size:50], NSFontAttributeName, nil, nil]];

   addressString = [[NSAttributedString alloc] initWithString:[owner address]
                                                   attributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSFont fontWithName:@"Helvetica" size:10],
                                                      NSFontAttributeName, nil, nil]];
}

- (void)setIsGhostView:(BOOL)isGhost
{
   isGhostView = isGhost;
}

- (BOOL)isGhostView
{
   return isGhostView;
}

- (BOOL)isSelected
{
   return isSelected;
}

- (void)setSelected
{
   isSelected = YES;
   [self setNeedsDisplay:YES];
}

- (void)setUnselected
{
   isSelected = NO;
   [self setNeedsDisplay:YES];
}

- (void)setDraggingMode:(int)mode
{
   draggingMode = mode;
}

//***********************************
- (void)setFrameSize:(NSSize)newSize
{
   [super setFrameSize:newSize];

   if([self displayMode] == SSOverlapDisplayMode) {
      //grow or shrink our fields to fit
      [actionField setFrameSize:NSMakeSize((NSWidth([self frame]) - 6), SSEventFieldOverlapHeight)];

      if(beginDateField) {
         [beginDateField setFrame:NSMakeRect(3, (SSDistanceForOverlapEventFields + 3), ((NSWidth([self frame]) / 2) - 4.5), SSEventFieldOverlapHeight)];
      }

      if(endDateField) {
         [endDateField setFrame:NSMakeRect(((NSWidth([self frame]) / 2) + 1.5), (SSDistanceForOverlapEventFields + 3), ((NSWidth([self frame]) / 2) - 4.5), SSEventFieldOverlapHeight)];
      }
   }
}

//***********************************
- (void)updateDisplayValues
{
   [self updateActor];
   [self updateAction];
   [self updateSource];
   [self updateObject];
   [self updateLocation];
   [self updateBeginTime];
   [self updateEndTime];
}

- (void)updateActor
{
   if([[NSUserDefaults standardUserDefaults] boolForKey:SSDisplayEBActorKey] && ssDisplayMode == SSMatrixDisplayMode) {
      [actorField setStringValue:[owner actor]];
   }
}

- (void)updateAction
{
   if(ssDisplayMode == SSMatrixDisplayMode) {
      [actionField setStringValue:[owner action]];
   } else {
      //in overlap
      [actionField setStringValue:[NSString stringWithFormat:@"%@ - %@", [owner action], [owner object]]];
   }
}


- (void)updateSource
{
  if([[NSUserDefaults standardUserDefaults] boolForKey:SSDisplayEBSourceKey] && ssDisplayMode == SSMatrixDisplayMode) {
     [sourceField setObjectValue:[owner source]];
  }
}

- (void)updateObject
{
   if(ssDisplayMode == SSMatrixDisplayMode) {
      if([[NSUserDefaults standardUserDefaults] boolForKey:SSDisplayEBObjectKey]) {
         [objectField setStringValue:[owner object]];
      }
   } else {
      //in overlap display 
      [actionField setStringValue:[NSString stringWithFormat:@"%@ - %@", [owner action], [owner object]]];
   }
}

- (void)updateLocation
{
   if([[NSUserDefaults standardUserDefaults] boolForKey:SSDisplayEBLocationKey] && ssDisplayMode == SSMatrixDisplayMode) {
      [locationField setStringValue:[owner location]];
   }
}

- (void)updateBeginTime
{
   if([[NSUserDefaults standardUserDefaults] boolForKey:SSDisplayEBBeginDateKey]) {
      [beginDateField setObjectValue:[owner beginTime]];
   }

   if([self displayMode] == SSOverlapDisplayMode) {
      //set our frame if needed
      float xPos = [(SSChartView *)[self superview] xPositionForDate:[owner beginTime]];
      if(NSMinX([self frame]) != xPos) {
         //date has been changed through the inspector, update our frame
         [self updateOverlapFrameAfterExternalDateChange];
      }
   }
}

- (void)updateEndTime
{

   if([[NSUserDefaults standardUserDefaults] boolForKey:SSDisplayEBEndDateKey]) {
      [endDateField setObjectValue:[owner endTime]];
   }
   
   if([self displayMode] == SSOverlapDisplayMode) {
      //set our frame if needed
      float xPos = [(SSChartView *)[self superview] xPositionForDate:[owner endTime]];
      if(NSMaxX([self frame]) != xPos) {
         //date has been changed through the inspector, update our frame
         [self updateOverlapFrameAfterExternalDateChange];
      }
   }
}

//***********************************
- (void)controlTextDidEndEditing:(NSNotification *)note
{
   //called when a text field stops editing
   switch([[note object] tag]) {
      case 0:
         if(![[[note object] objectValue] isEqualTo:[owner source]]) { //if it hasn't changed don't set the same value
            [owner setSource:[[note object] objectValue]];
         }
           break; 
      case 1:
         if(![[[note object] stringValue] isEqualToString:[owner actor]]) { //if it hasn't changed don't set the same value
            [owner setActor:[[note object] stringValue]];
         }
           break;
      case 2:
         if(![[[note object] stringValue] isEqualToString:[owner action]]) { //if it hasn't changed don't set the same value
            [owner setAction:[[note object] stringValue]];
         }
           break;
      case 3:
         if(![(SSChartDate *)[[note object] objectValue] isEqualTo:[owner beginTime]] || ([(SSChartDate *)[[note object] objectValue] isEstimated] != [[owner beginTime] isEstimated])) { //if it hasn't changed don't set the same value
            [owner setBeginTime:[[note object] objectValue]];
         }
         break;
      case 4:
         if(![(SSChartDate *)[[note object] objectValue] isEqualTo:[owner endTime]] || ([(SSChartDate *)[[note object] objectValue] isEstimated] != [[owner endTime] isEstimated])) { //if it hasn't changed don't set the same value
            [owner setEndTime:[[note object] objectValue]];
         }
         break;
      case 5:
         if(![[[note object] stringValue] isEqualToString:[owner object]]) { //if it hasn't changed don't set the same value
            [owner setObject:[[note object] stringValue]];
         }
         break;
      case 6:
         if(![[[note object] stringValue] isEqualToString:[owner location]]) { //if it hasn't changed don't set the same value
            [owner setLocation:[[note object] stringValue]];
         }
         break;
      default:
         break;
   }
}

- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string 
        errorDescription:(NSString *)error
{
   if([control isEqualTo:beginDateField] || [control isEqualTo:endDateField]) {
      //break the string into it's time segments
      NSString *appendedString = [string stringByTrimmingCharactersInSet:[NSCharacterSet letterCharacterSet]];
      appendedString = [appendedString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
      NSArray *whiteSplitArray = [appendedString componentsSeparatedByString:@" "];
      NSMutableArray *timeArray = [NSMutableArray array];
      NSString *timeString = [NSString string];
      int i = 0;
      int j = 0;

      while(j < [whiteSplitArray count]) {
         //loop through the whiteSplitArray and split its string objects
         [timeArray addObjectsFromArray:[[whiteSplitArray objectAtIndex:j] componentsSeparatedByString:@":"]];
         j++;
      }

      while(i < [timeArray count] && i < 7) {
         //include the < 7 incase the user has entered too many values
         timeString = [timeString stringByAppendingString:[timeArray objectAtIndex:i]]; //add the time value to the string

         if(i == 2) {
            //need to add a space
            timeString = [timeString stringByAppendingString:@" "];
         } else if(i < 6) {
            //add the seperator
            timeString = [timeString stringByAppendingString:@":"];
         }

         i++;
      }

      while(i < 7) {
         //we have used all of the user entered values so pad with 0
         timeString = [timeString stringByAppendingString:@"0"];

         if(i == 2) {
            //need to add a space
            timeString = [timeString stringByAppendingString:@" "];
         } else if(i < 6) {
            //add the seperator
            timeString = [timeString stringByAppendingString:@":"];
         }

         i++;
      }

      SSChartDate *cd = [[[SSChartDate alloc] initWithString:timeString calendarFormat:@"%Y:%m:%d %H:%M:%S:%F"] autorelease];

      if(cd) {
         //the new date is valid so set the fields value to that
         [control setObjectValue:cd];
      } else {
         //the user entered un usable info so set the date to now
         [control setObjectValue:[SSChartDate calendarDate]];
      }
   }

   return NO;
}


- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView
doCommandBySelector:(SEL)commandSelector
{
   if(commandSelector == @selector(insertTab:)) {
      //user pressed tab
      [self selectNextField];
      return YES;
   } else {
      if(commandSelector == @selector(insertNewline:)) {
         //user pressed return end editing
         [self setSelectedField:nil];
         return YES;
      } else {
         return NO;
      }
   }
}

//***********************************
- (void)allowEditingInFieldAtPoint:(NSPoint)point
{
   NSEnumerator *enumerator = [[self subviews] objectEnumerator];
   id anObject;
   point = [self convertPoint:point fromView:nil];

   while(anObject = [enumerator nextObject]) {
      if(point.x > NSMinX([anObject frame]) && point.x < NSMaxX([anObject frame])) {
         //it is within the x range of this object
         if(point.y > NSMinY([anObject frame]) && point.y < NSMaxY([anObject frame])) {
            //double click was above this field make it editable
            [self setSelectedField:anObject];
            break;
         }
      }
   }
}

- (void)setSelectedField:(SSEventTextField *)field
{
   //deselect the selected field
   if([[self selectedField] isKindOfClass:[SSEventTextField class]]) {
      [[self selectedField] setSelectable:NO];
      [[self selectedField] setBordered:NO];
      [[self window] makeFirstResponder:[self superview]];

      //change the selected field
      selectedField = field;
   } else {
      //selected field is not an SSEventTextField just set it to field
      selectedField = field;
   }

   if([[self selectedField] isKindOfClass:[SSEventTextField class]]) {
      //if a field has been passed select it
      [field setEditable:YES];
      [field selectText:self];
      [field setBordered:YES];
      [[self window] makeFirstResponder:field];
   }
}

- (SSEventTextField *)selectedField
{
   return selectedField;
}

- (void)selectNextField
{
   NSArray *fields = [[self subviews] sortedArrayUsingFunction:sortByYPos context:NULL]; //get a sorted array of our subviews
   SSEventTextField *lastObject = [fields lastObject];

   if([[self selectedField] isEqualTo:lastObject]) {
      //it's the last object so tab to the first
      [self setSelectedField:[fields objectAtIndex:0]];
   } else {
      //select the next object in the array
      [self setSelectedField:[fields objectAtIndex:([fields indexOfObject:[self selectedField]] + 1)]];
   }
}

//***********************************
- (void)setMovementUndo:(NSTimer *)timer
{
   NSUndoManager* aManager = [[[NSDocumentController sharedDocumentController] currentDocument] undoManager];
   [aManager registerUndoWithTarget:self selector:@selector(setNewOriginWithValue:) object:[NSValue valueWithPoint:NSMakePoint([self lastXPosition], NSMinY([self frame]))]];
   [self setLastXPosition];
}

- (void)setLastXPosition
{
   if([self displayMode] == SSMatrixDisplayMode) {
      lastXPosition = NSMinX([self frame]);
   }
}

- (float)lastXPosition
{
   return lastXPosition;
}

//***********************************
int sortByYPos( id obj1, id obj2, void *context )
{
   if(NSMinY([obj1 frame]) < NSMinY([obj2 frame])) {
      return NSOrderedAscending;
   } else {
      if(NSMinY([obj1 frame]) > NSMinY([obj2 frame])) {
         return NSOrderedDescending;
      } else {
         return NSOrderedSame;
      }
   }
}

//***********************************

- (BOOL)isFullyWithinRect:(NSRect)rect
{
   NSPoint origin = [[self superview] convertPoint:[self bounds].origin fromView:self];

   if(origin.x > NSMinX(rect)) { //it's ok on the left
      if(origin.x + NSWidth([self bounds]) < NSMaxX(rect)) { //it's ok on the right
         if(origin.y > NSMinY(rect)) { //it's ok at the bottom
            if(origin.y + NSHeight([self bounds]) < NSMaxY(rect)) { //it's ok at top
                                                                    //it's fullyWithinRect
               return YES;
            }
         }
      }
   }
   return NO;
}

//***********************************
- (void)setIsLinking:(BOOL)linking
{
   isLinking = linking;
}

- (BOOL)isLinking
{
   return isLinking;
}

//***********************************
- (NSMenu *)menuForEvent:(NSEvent *)theEvent
{
   return [self menu];
}

- (NSMenu *)menu
{
   NSMenu *menu = [[[NSMenu alloc] initWithTitle:@" "] autorelease];
   NSMenuItem *linkFromItem = [[[NSMenuItem alloc] init] autorelease];
   NSMenuItem *linkToItem = [[[NSMenuItem alloc] init] autorelease];
   NSMenuItem *previousItem = [[[NSMenuItem alloc] init] autorelease];
   NSMenuItem *nextItem = [[[NSMenuItem alloc] init] autorelease];

   [linkFromItem setTitle:NSLocalizedString(@"SetLinkFromEvent", nil)];
   [linkFromItem setTarget:[[NSDocumentController sharedDocumentController] currentDocument]];
   [linkFromItem setAction:@selector(setCurrentLinkFromEvent:)];
   [linkFromItem setRepresentedObject:[self owner]];
   [menu addItem:linkFromItem];
   [linkToItem setTitle:NSLocalizedString(@"SetLinkToEvent", nil)];
   [linkToItem setTarget:[[[NSDocumentController sharedDocumentController] currentDocument] currentLinkFromEvent]];
   [linkToItem setAction:@selector(addLinkToObjectMenuClick:)];
   [linkToItem setRepresentedObject:[self owner]];
   [menu addItem:linkToItem];

   [menu addItem:[NSMenuItem separatorItem]];

   [previousItem setTitle:NSLocalizedString(@"SnapToPreviousEvent", nil)];
   [previousItem setTarget:[[NSDocumentController sharedDocumentController] currentDocument]];
   [previousItem setAction:@selector(setIncrementedTimeForEBBasedOnPrevious:)];
   [previousItem setRepresentedObject:[self owner]];
   [menu addItem:previousItem];
   [nextItem setTitle:NSLocalizedString(@"SnapToNextEvent", nil)];
   [nextItem setTarget:[[NSDocumentController sharedDocumentController] currentDocument]];
   [nextItem setAction:@selector(setIncrementedTimeForEBBasedOnNext:)];
   [nextItem setRepresentedObject:[self owner]];
   [menu addItem:nextItem];

   return menu;
}

//the menu code below will create a menu for manipulation the EBs links
//- (NSMenu *)menu
//{
  // SSEventLinkObject *anObject;
//   NSEnumerator *enumerator = [[owner linkArray] objectEnumerator];

  // NSMenu *mainMenu = [[NSMenu alloc] initWithTitle:@"mainMenu"];
//   NSMenu *removeMenu = [[NSMenu alloc] initWithTitle:@"removeMenu"];
  // NSMenu *changeMenu = [[NSMenu alloc] initWithTitle:@"changeMenu"];
//   NSMenuItem *removeItem = [[NSMenuItem alloc] init];
  // NSMenuItem *changeItem = [[NSMenuItem alloc] init];
//   NSMenuItem *item = [[NSMenuItem alloc] init];

   //set the 2 top level items
  // [removeItem setTitle:NSLocalizedString(@"RemoveLink", nil)];
//   [mainMenu addItem:removeItem];
  // [removeItem release];

//   [changeItem setTitle:NSLocalizedString(@"ChangeLinkStatus", nil)];
  // [mainMenu addItem:changeItem];
//   [changeItem release];
   
  // while ((anObject = [enumerator nextObject])) { //loop through links and build items for them
      //set the remove menu item for this connection
    //  item = [[NSMenuItem alloc] init];
      //[item setTitle:[NSString stringWithFormat:@"%@ - %@", [(SSEventObject *)[anObject linkedTo] actor], [(SSEventObject *)[anObject linkedTo] action]]]; //build a string with the actor-action of connected object
//      [item setTarget:[[NSDocumentController sharedDocumentController] currentDocument]];
  //    [item setAction:@selector(deleteObject:)];
    //  [item setRepresentedObject:anObject];
      //[removeMenu addItem:item];
//      [item release];

      //set the change type menu item for this connection
  //    item = [[NSMenuItem alloc] init];
    //  [item setTitle:[NSString stringWithFormat:@"%@ - %@", [(SSEventObject *)[anObject linkedTo] actor], [(SSEventObject *)[anObject linkedTo] action]]]; //build a string with the actor-action of connected object
      //[item setTarget:anObject];
//      [item setAction:@selector(changeLinkType)];
  //    [changeMenu addItem:item];
    //  [item release];
//   }

//   [mainMenu setSubmenu:removeMenu forItem:removeItem];
  // [mainMenu setSubmenu:changeMenu forItem:changeItem];

//   [mainMenu autorelease];
  // [removeMenu autorelease];
//   [changeMenu autorelease];

  // return mainMenu;
//}

//***********************************
//   these methods return a position to set the connecting lines
//***********************************
- (void)setConnectionPoints
{
   //taken from chartConstructor sets points from around the view which are used to set link position

   leftPoint = NSMakePoint([self frame].origin.x + .5,
                           ([self frame].origin.y + ([self frame].size.height / 2) + .5));

   leftTopPoint = NSMakePoint([self frame].origin.x + .5,
                              ([self frame].origin.y + ([self frame].size.height / 4) + .5));

   leftBottomPoint = NSMakePoint([self frame].origin.x + .5,
                                 ([self frame].origin.y + (3 * [self frame].size.height / 4) + .5));

   rightPoint = NSMakePoint(([self frame].origin.x + [self frame].size.width) + .5,
                            ([self frame].origin.y + ([self frame].size.height / 2) + .5));

   rightTopPoint = NSMakePoint(([self frame].origin.x + [self frame].size.width) + .5,
                               ([self frame].origin.y + ([self frame].size.height / 4) + .5));

   rightBottomPoint = NSMakePoint(([self frame].origin.x + [self frame].size.width) + .5,
                                  ([self frame].origin.y + (3 * [self frame].size.height / 4) + .5));

   topPoint = NSMakePoint(([self frame].origin.x + ([self frame].size.width / 2)) + .5,
                          [self frame].origin.y + .5);

   bottomPoint = NSMakePoint(([self frame].origin.x + ([self frame].size.width / 2) + .5),
                             ([self frame].origin.y + [self frame].size.height) + .5);

   centerPoint = NSMakePoint(([self frame].origin.x + ([self frame].size.width / 2) + .5),
                             ([self frame].origin.y + ([self frame].size.height) / 2) + .5);
   
   //these points need to be different depending on our mode
   if([self displayMode] == SSMatrixDisplayMode) {
      topLeftPoint = NSMakePoint(([self frame].origin.x + ([self frame].size.width / 4) + .5),
                                 [self frame].origin.y + .5);

      topRightPoint = NSMakePoint(([self frame].origin.x + (3 * [self frame].size.width / 4) + .5),
                                  [self frame].origin.y + .5);

      bottomLeftPoint = NSMakePoint(([self frame].origin.x + ([self frame].size.width / 4) + .5),
                                    ([self frame].origin.y + [self frame].size.height) + .5);

      bottomRightPoint = NSMakePoint(([self frame].origin.x + (3 * [self frame].size.width / 4) + .5),
                                     ([self frame].origin.y + [self frame].size.height) + .5);
   } else {
      //in overlap
      topLeftPoint = NSMakePoint(([self frame].origin.x + .5),
                                 [self frame].origin.y + .5);

      topRightPoint = NSMakePoint(NSMaxX([self frame]) - .5,
                                  [self frame].origin.y + .5);

      bottomLeftPoint = NSMakePoint(([self frame].origin.x + .5),
                                    ([self frame].origin.y + [self frame].size.height) + .5);

      bottomRightPoint = NSMakePoint(NSMaxX([self frame]) - .5,
                                     ([self frame].origin.y + [self frame].size.height) + .5);
      
   }

   [owner updateLinks];
}

- (NSPoint)leftPoint //center left
{
   return leftPoint;
}

- (NSPoint)leftTopPoint //top left
{
   return leftTopPoint;
}

- (NSPoint)leftBottomPoint //bottom left
{
   return leftBottomPoint;
}

- (NSPoint)rightPoint //center right
{
   return rightPoint;
}

- (NSPoint)rightTopPoint //top right
{
   return rightTopPoint;
}

- (NSPoint)rightBottomPoint //bottom right
{
   return rightBottomPoint;
}

- (NSPoint)topPoint //center top
{
   return topPoint;
}

- (NSPoint)topLeftPoint //top left
{
   return topLeftPoint;
}

- (NSPoint)topRightPoint //top right
{
   return topRightPoint;
}

- (NSPoint)bottomPoint //center bottom
{
   return bottomPoint;
}

- (NSPoint)bottomLeftPoint //bottom left
{
   return bottomLeftPoint;
}

- (NSPoint)bottomRightPoint //bottom right
{
   return bottomRightPoint;
}

- (NSPoint)centerPoint
{
   return centerPoint;
}

//***********************************
- (NSColor *)color
{
   switch([[owner label] intValue]) {
      case SSYellowLabelType:return SSYellowColor;
         break;
      case SSGreenLabelType:return SSGreenColor;
         break;
      case SSLightBlueLabelType:return SSLightBlueColor;
         break;
      case SSBlueLabelType:return SSBlueColor;
         break;
      case SSPurpleLabelType:return SSPurpleColor;
         break;
      case SSOrangeLabelType:return SSOrangeColor;
         break;
      case SSRedLabelType:return SSRedColor;
         break;
      default:
         return SSWhiteColor;
         break;
   }
}
//***********************************

- (NSComparisonResult)compareDateToEventView:(SSEventView *)eventView
{
   NSComparisonResult result = [(SSChartDate *)[owner beginTime] compare:(SSChartDate *)[[eventView owner] beginTime]];

   return result;
}

//***********************************

- (BOOL)isFlipped
{
   //changes the coordinate system so 0,0 is in the top left
   return YES;
}

- (BOOL)isOpaque
{
   // State that the view is opaque for optimization purposes.
   return YES;
}

//***********************************
//- (NSString *)description
//{
  // return [NSString stringWithFormat:@"[borderPath %@]--[gridPath %@]--[fields sourceField %@ actorField %@ actionField %@ beginDateField %@ endDateField %@ objectField %@ locationField %@ selectedField %@]--[dateFormatter %@]",  borderPath, gridPath, sourceField, actorField, actionField, beginDateField, endDateField, objectField, locationField, selectedField, dateFormatter];
//}
//***********************************coding

- (void)encodeWithCoder:(NSCoder *)coder
{
   [super encodeWithCoder:coder];
   [coder encodeObject:owner forKey:@"owner"];
   [coder encodeBool:isGhostView forKey:@"isGhostView"];

   if([self displayMode] == SSMatrixDisplayMode) {
      [coder encodeFloat:NSMinX([self frame]) forKey:@"matrixDisplayMinX"];
   } else {
      [coder encodeFloat:lastXPosition forKey:@"matrixDisplayMinX"];
   }
}

- (id)initWithCoder:(NSCoder *)coder
{
   if(self = [super initWithCoder:coder]) {
      [self setOwner:[coder decodeObjectForKey:@"owner"]];
      [self setIsGhostView:[coder decodeBoolForKey:@"isGhostView"]];

      if([coder containsValueForKey:@"matrixDisplayMinX"]) {
         [self setFrameOrigin:NSMakePoint([coder decodeFloatForKey:@"matrixDisplayMinX"], NSMinY([self frame]))];
         [self setLastXPosition];
      } else {
         [self setLastXPosition];
      }

      [self setPostsFrameChangedNotifications:YES]; //post a notification when our frame changes

      dateFormatter = [[SSEBDateFormatter alloc] initWithDateFormat:[NSString stringWithFormat:@"%@:%@:%@ %@:%@:%@:%@",
         @"%Y", @"%m", @"%d", @"%H", @"%M", @"%S", @"%F"] allowNaturalLanguage:YES];


      NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
      [nc addObserver:self
             selector:@selector(setConnectionPoints)
                 name:@"NSViewFrameDidChangeNotification"
               object:self];
      [nc addObserver:self
             selector:@selector(setupForDisplayAfterPreferenceChange:)
                 name:@"SSUpdateEBDisplay"
               object:nil];
      
      [self setupForDisplay];
   }

   return self;
}

//***********************************

- (void)dealloc
{
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   [questionString release];
   [addressString release];
   [borderPath release];
   [gridPath release];
   [dateFormatter release];
   [sourceFormatter release];
   [undoMovementTimer release];
   
   [super dealloc];
}


@end
